import sys
import os
import cv2
import numpy as np
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                             QLabel, QPushButton, QFileDialog, QSlider, QSpinBox, QComboBox,
                             QTabWidget, QGroupBox, QGridLayout, QSplitter, QMessageBox,
                             QProgressBar, QListWidget, QAction, QMenu, QToolBar, QDockWidget,
                             QInputDialog, QDialog, QScrollArea, QFrame, QSizePolicy)
from PyQt5.QtGui import (QPixmap, QImage, QPainter, QPen, QColor, QFont, QContextMenuEvent,
                         QIcon)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QPoint, QRect, QSize, QUrl, QTimer
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtMultimediaWidgets import QVideoWidget


class ImageProcessor:
    """图像处理核心类，提供各种图像处理功能"""

    def __init__(self):
        self.image = None
        self.original_image = None
        self.processed_image = None
        self.detected_shapes = []
        self.faces = []
        self.face_templates = {}
        self.history = []  # 用于撤销操作的历史记录
        self.history_index = -1

    def load_image(self, file_path):
        """加载图像文件"""
        self.original_image = cv2.imread(file_path)
        if self.original_image is None:
            return False
        self.image = self.original_image.copy()
        self.processed_image = self.image.copy()
        self._save_to_history()
        return True

    def rotate(self, angle):
        """旋转图像"""
        if self.image is None:
            return
        (h, w) = self.image.shape[:2]
        center = (w // 2, h // 2)
        M = cv2.getRotationMatrix2D(center, angle, 1.0)
        self.image = cv2.warpAffine(self.image, M, (w, h))
        self.processed_image = self.image.copy()
        self._save_to_history()

    def scale(self, scale_percent):
        """缩放图像"""
        if self.image is None:
            return
        width = int(self.image.shape[1] * scale_percent / 100)
        height = int(self.image.shape[0] * scale_percent / 100)
        dim = (width, height)
        self.image = cv2.resize(self.image, dim, interpolation=cv2.INTER_AREA)
        self.processed_image = self.image.copy()
        self._save_to_history()

    def crop(self, x, y, w, h):
        """裁剪图像"""
        if self.image is None:
            return
        self.image = self.image[y:y + h, x:x + w]
        self.processed_image = self.image.copy()
        self._save_to_history()

    def adjust_brightness_contrast(self, brightness=0, contrast=0):
        """调整亮度和对比度"""
        if self.image is None:
            return
        if brightness != 0:
            if brightness > 0:
                shadow = brightness
                highlight = 255
            else:
                shadow = 0
                highlight = 255 + brightness
            alpha_b = (highlight - shadow) / 255
            gamma_b = shadow

            buf = cv2.addWeighted(self.image, alpha_b, self.image, 0, gamma_b)
        else:
            buf = self.image.copy()

        if contrast != 0:
            f = 131 * (contrast + 127) / (127 * (131 - contrast))
            alpha_c = f
            gamma_c = 127 * (1 - f)

            buf = cv2.addWeighted(buf, alpha_c, buf, 0, gamma_c)

        self.processed_image = buf

    def adjust_saturation(self, value):
        """调整饱和度"""
        if self.image is None:
            return
        value = value / 100.0  # 将百分比转换为系数
        hsv = cv2.cvtColor(self.image, cv2.COLOR_BGR2HSV)
        h, s, v = cv2.split(hsv)
        s = np.clip(s * value, 0, 255).astype(hsv.dtype)
        hsv = cv2.merge([h, s, v])
        self.processed_image = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

    def smooth(self, kernel_size=3):
        """平滑处理"""
        if self.image is None:
            return
        self.processed_image = cv2.GaussianBlur(self.image, (kernel_size, kernel_size), 0)

    def detect_shapes(self):
        """检测图像中的形状"""
        self.detected_shapes = []
        if self.image is None:
            return

        gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
        blurred = cv2.GaussianBlur(gray, (5, 5), 0)
        thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY)[1]

        contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        self.processed_image = self.image.copy()

        for c in contours:
            # 计算轮廓面积
            area = cv2.contourArea(c)
            if area < 100:  # 忽略小面积
                continue

            # 计算轮廓周长
            perimeter = cv2.arcLength(c, True)
            # 多边形逼近
            approx = cv2.approxPolyDP(c, 0.04 * perimeter, True)

            shape = "unidentified"
            if len(approx) == 3:
                shape = "triangle"
            elif len(approx) == 4:
                (x, y, w, h) = cv2.boundingRect(approx)
                ar = w / float(h)
                shape = "square" if 0.95 <= ar <= 1.05 else "rectangle"
            elif len(approx) > 4:
                shape = "circle"

            self.detected_shapes.append((shape, approx))

            # 绘制轮廓和形状名称
            cv2.drawContours(self.processed_image, [c], -1, (0, 255, 0), 2)
            M = cv2.moments(c)
            if M["m00"] != 0:
                cX = int(M["m10"] / M["m00"])
                cY = int(M["m01"] / M["m00"])
                cv2.putText(self.processed_image, shape, (cX, cY),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)

    def load_face_template(self, name, file_path):
        """加载人脸模板"""
        template = cv2.imread(file_path)
        if template is not None:
            gray_template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
            face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
            faces = face_cascade.detectMultiScale(gray_template, 1.1, 4)
            if len(faces) > 0:
                (x, y, w, h) = faces[0]
                face_roi = gray_template[y:y + h, x:x + w]
                self.face_templates[name] = face_roi
                return True
        return False

    def detect_faces(self):
        """检测人脸"""
        self.faces = []
        if self.image is None:
            return

        gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
        face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
        faces = face_cascade.detectMultiScale(gray, 1.1, 4)

        self.processed_image = self.image.copy()

        for (x, y, w, h) in faces:
            face_roi = gray[y:y + h, x:x + w]
            best_match = None
            best_score = 0

            # 人脸匹配
            for name, template in self.face_templates.items():
                if face_roi.shape[0] < template.shape[0] or face_roi.shape[1] < template.shape[1]:
                    continue

                result = cv2.matchTemplate(face_roi, template, cv2.TM_CCOEFF_NORMED)
                min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)

                if max_val > best_score:
                    best_score = max_val
                    best_match = name

            self.faces.append((x, y, w, h, best_match, best_score))

            # 绘制人脸矩形
            color = (0, 255, 0) if best_match is None else (0, 0, 255)
            cv2.rectangle(self.processed_image, (x, y), (x + w, y + h), color, 2)

            # 添加匹配信息
            if best_match is not None:
                text = f"{best_match}: {best_score:.2f}"
                cv2.putText(self.processed_image, text, (x, y - 10),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)

    def extract_face_features(self, image=None):
        """提取人脸特征"""
        if image is None:
            if self.image is None:
                return None
            img = self.image
        else:
            img = image

        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
        faces = face_cascade.detectMultiScale(gray, 1.1, 4)

        face_features = []

        for (x, y, w, h) in faces:
            face_roi = gray[y:y + h, x:x + w]
            face_features.append(face_roi)

        return face_features

    def stitch_images(self, images):
        """图像拼接功能"""
        if len(images) < 2:
            return None

        stitcher = cv2.Stitcher_create() if cv2.__version__.startswith('4') else cv2.createStitcher()
        status, result = stitcher.stitch(images)

        if status == cv2.Stitcher_OK:
            self.processed_image = result
            return result
        else:
            print(f"图像拼接失败，错误代码: {status}")
            return None

    def _save_to_history(self):
        """保存当前图像状态到历史记录"""
        # 清除历史记录中当前状态之后的所有状态
        if self.history_index < len(self.history) - 1:
            self.history = self.history[:self.history_index + 1]

        # 保存当前图像的副本
        self.history.append(self.image.copy())
        self.history_index = len(self.history) - 1

        # 限制历史记录长度
        if len(self.history) > 20:
            self.history.pop(0)
            self.history_index -= 1

    def undo(self):
        """撤销上一步操作"""
        if self.history_index > 0:
            self.history_index -= 1
            self.image = self.history[self.history_index].copy()
            self.processed_image = self.image.copy()
            return True
        return False


class VideoProcessor:
    """视频处理核心类，提供视频播放和处理功能"""

    def __init__(self):
        # 使用QMediaPlayer的原始视频播放器
        self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        # 使用OpenCV的备用视频播放器
        self.cap = None
        self.image_processor = ImageProcessor()
        self.frame = None
        self.processing_enabled = False
        self.processing_function = None
        self.use_opencv = False  # 是否使用OpenCV播放视频
        self.current_file = None
        self.total_frames = 0
        self.current_frame = 0
        self.fps = 30  # 默认帧率

    def load_video(self, file_path):
        """加载视频文件"""
        self.current_file = file_path

        # 尝试使用QMediaPlayer加载视频
        self.player.setMedia(QMediaContent(QUrl.fromLocalFile(file_path)))

        # 检查视频格式
        file_ext = os.path.splitext(file_path)[1].lower()
        supported_formats = ['.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv']

        if file_ext not in supported_formats:
            QMessageBox.warning(None, '不支持的格式',
                                f'文件格式 {file_ext} 可能不受支持。\n'
                                '支持的格式: ' + ', '.join(supported_formats))
            return False

        # 尝试使用OpenCV打开视频作为备用
        self.cap = cv2.VideoCapture(file_path)
        if not self.cap.isOpened():
            QMessageBox.warning(None, '错误', '无法打开视频文件')
            self.use_opencv = False
            return False
        else:
            self.use_opencv = True
            self.total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
            self.fps = self.cap.get(cv2.CAP_PROP_FPS)
            self.cap.release()

        return True

    def process_frame(self, frame):
        """处理视频帧"""
        if not self.processing_enabled or self.processing_function is None:
            return frame

        self.image_processor.image = frame
        self.processing_function()
        return self.image_processor.processed_image

    def play(self):
        """播放视频"""
        if self.use_opencv and self.current_file:
            # 使用OpenCV播放
            if self.cap is None or not self.cap.isOpened():
                self.cap = cv2.VideoCapture(self.current_file)
                self.current_frame = 0
            return self.cap.isOpened()
        else:
            # 使用QMediaPlayer播放
            self.player.play()
            return True

    def pause(self):
        """暂停视频"""
        if self.use_opencv and self.cap and self.cap.isOpened():
            # OpenCV没有直接的暂停方法，这里只是标记
            pass
        else:
            self.player.pause()

    def stop(self):
        """停止视频"""
        if self.use_opencv and self.cap:
            self.cap.release()
            self.cap = None
            self.current_frame = 0
        else:
            self.player.stop()

    def read_next_frame(self):
        """读取下一帧"""
        if self.use_opencv and self.cap and self.cap.isOpened():
            ret, frame = self.cap.read()
            if ret:
                self.current_frame += 1
                if self.current_frame >= self.total_frames:
                    self.current_frame = 0
            else:
                # 视频结束，重新打开以循环播放
                self.cap.release()
                self.cap = cv2.VideoCapture(self.current_file)
                self.current_frame = 0
                ret, frame = self.cap.read()
                if ret:
                    self.current_frame += 1
            return ret, frame
        return False, None

    def set_processing_function(self, func):
        """设置处理函数"""
        self.processing_function = func

    def toggle_processing(self, enable):
        """切换处理状态"""
        self.processing_enabled = enable


class ProcessingThread(QThread):
    """视频处理线程，用于后台处理视频帧"""

    frame_ready = pyqtSignal(np.ndarray)
    progress_updated = pyqtSignal(int, int)

    def __init__(self, video_processor):
        super().__init__()
        self.video_processor = video_processor
        self.running = True

    def run(self):
        """线程运行函数"""
        while self.running:
            if self.video_processor.use_opencv:
                ret, frame = self.video_processor.read_next_frame()
                if ret:
                    processed_frame = self.video_processor.process_frame(frame)
                    self.frame_ready.emit(processed_frame)
                    self.progress_updated.emit(self.video_processor.current_frame, self.video_processor.total_frames)
                    # 控制帧率
                    self.msleep(int(1000 / self.video_processor.fps))
                else:
                    self.msleep(100)  # 没有帧时等待
            else:
                # 使用QMediaPlayer时不需要在这里处理帧
                self.msleep(100)

    def stop(self):
        """停止线程"""
        self.running = False
        self.wait()


class VideoWidget(QVideoWidget):
    """视频显示窗口，用于显示图像和视频帧"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self.image = None
        self.setMinimumSize(640, 480)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

    def paintEvent(self, event):
        """绘制事件"""
        super().paintEvent(event)
        if self.image:
            painter = QPainter(self)
            painter.drawImage(0, 0, self.image)

    def setImage(self, image):
        """设置要显示的图像"""
        self.image = image
        self.update()


class FaceTemplateDialog(QDialog):
    """人脸模板选择对话框"""

    def __init__(self, face_features, parent=None):
        super().__init__(parent)
        self.setWindowTitle('选择人脸模板')
        self.face_features = face_features
        self.selected_index = -1

        layout = QVBoxLayout(self)

        scroll_area = QScrollArea()
        scroll_content = QWidget()
        scroll_layout = QVBoxLayout(scroll_content)

        for i, face in enumerate(face_features):
            face_widget = QWidget()
            face_layout = QHBoxLayout(face_widget)

            # 将OpenCV图像转换为QImage显示
            height, width = face.shape
            bytes_per_line = width
            qimage = QImage(face.data, width, height, bytes_per_line, QImage.Format_Grayscale8)
            label = QLabel()
            label.setPixmap(QPixmap.fromImage(qimage).scaled(100, 100, Qt.KeepAspectRatio))

            btn = QPushButton(f'选择人脸 {i + 1}')
            btn.clicked.connect(lambda checked, idx=i: self.select_face(idx))

            face_layout.addWidget(label)
            face_layout.addWidget(btn)
            scroll_layout.addWidget(face_widget)

        scroll_area.setWidget(scroll_content)
        scroll_area.setWidgetResizable(True)
        layout.addWidget(scroll_area)

    def select_face(self, index):
        """选择人脸"""
        self.selected_index = index
        self.accept()


class MainWindow(QMainWindow):
    """主窗口类，程序入口点"""

    def __init__(self):
        super().__init__()
        self.image_processor = ImageProcessor()
        self.video_processor = VideoProcessor()
        self.init_ui()

        # 初始化摄像头
        self.video_capture = None
        self.processing_thread = None

        # 初始化裁剪区域
        self.crop_start = None
        self.crop_end = None
        self.is_cropping = False

        # 图像拼接相关
        self.stitch_images = []

        # 连接视频播放器信号
        self.video_processor.player.stateChanged.connect(self.video_state_changed)
        self.video_processor.player.error.connect(self.video_error)

    def init_ui(self):
        """初始化用户界面"""
        self.setWindowTitle('图像处理与计算机视觉平台')
        self.setGeometry(100, 100, 1200, 800)

        # 创建菜单栏
        self.create_menu_bar()

        # 创建工具栏
        self.create_tool_bar()

        # 创建中央部件
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.main_layout = QHBoxLayout(self.central_widget)

        # 创建分割器
        self.splitter = QSplitter(Qt.Horizontal)
        self.main_layout.addWidget(self.splitter)

        # 创建左侧面板
        self.left_panel = QWidget()
        self.left_layout = QVBoxLayout(self.left_panel)
        self.splitter.addWidget(self.left_panel)

        # 创建选项卡控件
        self.tabs = QTabWidget()
        self.left_layout.addWidget(self.tabs)

        # 创建图像处理选项卡
        self.create_image_processing_tab()

        # 创建计算机视觉选项卡
        self.create_cv_tab()

        # 创建视频处理选项卡
        self.create_video_tab()

        # 创建右侧显示区域
        self.right_panel = QWidget()
        self.right_layout = QVBoxLayout(self.right_panel)
        self.splitter.addWidget(self.right_panel)

        # 创建图像/视频显示区域
        self.display_widget = VideoWidget()
        self.right_layout.addWidget(self.display_widget)

        # 创建状态栏
        self.statusBar().showMessage('就绪')

        # 设置分割器比例
        self.splitter.setSizes([300, 900])

        # 创建视频进度条
        self.create_video_progress_bar()

    def create_menu_bar(self):
        """创建菜单栏"""
        menubar = self.menuBar()

        # 文件菜单
        file_menu = menubar.addMenu('文件')

        # 加载图像动作
        load_image_action = QAction('加载图像', self)
        load_image_action.triggered.connect(self.load_image)
        file_menu.addAction(load_image_action)

        # 加载多张图像用于拼接
        load_images_action = QAction('加载多张图像(拼接)', self)
        load_images_action.triggered.connect(self.load_images_for_stitching)
        file_menu.addAction(load_images_action)

        # 加载视频动作
        load_video_action = QAction('加载视频', self)
        load_video_action.triggered.connect(self.load_video)
        file_menu.addAction(load_video_action)

        # 加载人脸模板动作
        load_face_template_action = QAction('加载人脸模板', self)
        load_face_template_action.triggered.connect(self.load_face_template)
        file_menu.addAction(load_face_template_action)

        # 保存图像动作
        save_image_action = QAction('保存图像', self)
        save_image_action.triggered.connect(self.save_image)
        file_menu.addAction(save_image_action)

        # 退出动作
        exit_action = QAction('退出', self)
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        # 编辑菜单
        edit_menu = menubar.addMenu('编辑')

        # 撤销动作
        undo_action = QAction('撤销', self)
        undo_action.triggered.connect(self.undo)
        edit_menu.addAction(undo_action)

        # 视图菜单
        view_menu = menubar.addMenu('视图')

        # 全屏动作
        fullscreen_action = QAction('全屏', self)
        fullscreen_action.triggered.connect(self.toggle_fullscreen)
        view_menu.addAction(fullscreen_action)

    def create_tool_bar(self):
        """创建工具栏"""
        toolbar = QToolBar('工具栏')
        self.addToolBar(toolbar)

        # 加载图像按钮
        load_image_btn = QAction('加载图像', self)
        load_image_btn.triggered.connect(self.load_image)
        toolbar.addAction(load_image_btn)

        # 加载视频按钮
        load_video_btn = QAction('加载视频', self)
        load_video_btn.triggered.connect(self.load_video)
        toolbar.addAction(load_video_btn)

        # 分割线
        toolbar.addSeparator()

        # 旋转按钮
        rotate_left_btn = QAction('左转', self)
        rotate_left_btn.triggered.connect(lambda: self.rotate_image(-90))
        toolbar.addAction(rotate_left_btn)

        rotate_right_btn = QAction('右转', self)
        rotate_right_btn.triggered.connect(lambda: self.rotate_image(90))
        toolbar.addAction(rotate_right_btn)

        # 分割线
        toolbar.addSeparator()

        # 摄像头按钮
        camera_btn = QAction('打开摄像头', self)
        camera_btn.triggered.connect(self.toggle_camera)
        toolbar.addAction(camera_btn)

    def create_image_processing_tab(self):
        """创建图像处理选项卡"""
        tab = QWidget()
        layout = QVBoxLayout(tab)

        # 旋转组
        rotate_group = QGroupBox('旋转')
        rotate_layout = QHBoxLayout(rotate_group)

        self.rotate_angle = QSpinBox()
        self.rotate_angle.setRange(-360, 360)
        self.rotate_angle.setValue(0)
        rotate_layout.addWidget(QLabel('角度:'))
        rotate_layout.addWidget(self.rotate_angle)

        rotate_btn = QPushButton('旋转')
        rotate_btn.clicked.connect(self.rotate_image)
        rotate_layout.addWidget(rotate_btn)

        layout.addWidget(rotate_group)

        # 缩放组
        scale_group = QGroupBox('缩放')
        scale_layout = QHBoxLayout(scale_group)

        self.scale_percent = QSpinBox()
        self.scale_percent.setRange(10, 500)
        self.scale_percent.setValue(100)
        scale_layout.addWidget(QLabel('百分比:'))
        scale_layout.addWidget(self.scale_percent)

        scale_btn = QPushButton('缩放')
        scale_btn.clicked.connect(self.scale_image)
        scale_layout.addWidget(scale_btn)

        layout.addWidget(scale_group)

        # 裁剪组
        crop_group = QGroupBox('裁剪')
        crop_layout = QHBoxLayout(crop_group)

        crop_btn = QPushButton('开始裁剪')
        crop_btn.clicked.connect(self.start_crop)
        crop_layout.addWidget(crop_btn)

        confirm_crop_btn = QPushButton('确认裁剪')
        confirm_crop_btn.clicked.connect(self.confirm_crop)
        crop_layout.addWidget(confirm_crop_btn)

        layout.addWidget(crop_group)

        # 亮度/对比度组
        brightness_contrast_group = QGroupBox('亮度/对比度')
        brightness_contrast_layout = QGridLayout(brightness_contrast_group)

        brightness_contrast_layout.addWidget(QLabel('亮度:'), 0, 0)
        self.brightness_slider = QSlider(Qt.Horizontal)
        self.brightness_slider.setRange(-100, 100)
        self.brightness_slider.setValue(0)
        self.brightness_slider.valueChanged.connect(self.adjust_brightness_contrast)
        brightness_contrast_layout.addWidget(self.brightness_slider, 0, 1)
        self.brightness_value = QLabel('0')
        brightness_contrast_layout.addWidget(self.brightness_value, 0, 2)

        brightness_contrast_layout.addWidget(QLabel('对比度:'), 1, 0)
        self.contrast_slider = QSlider(Qt.Horizontal)
        self.contrast_slider.setRange(-100, 100)
        self.contrast_slider.setValue(0)
        self.contrast_slider.valueChanged.connect(self.adjust_brightness_contrast)
        brightness_contrast_layout.addWidget(self.contrast_slider, 1, 1)
        self.contrast_value = QLabel('0')
        brightness_contrast_layout.addWidget(self.contrast_value, 1, 2)

        layout.addWidget(brightness_contrast_group)

        # 饱和度组
        saturation_group = QGroupBox('饱和度')
        saturation_layout = QHBoxLayout(saturation_group)

        self.saturation_slider = QSlider(Qt.Horizontal)
        self.saturation_slider.setRange(0, 300)
        self.saturation_slider.setValue(100)
        self.saturation_slider.valueChanged.connect(self.adjust_saturation)
        saturation_layout.addWidget(self.saturation_slider)

        self.saturation_value = QLabel('100%')
        saturation_layout.addWidget(self.saturation_value)

        layout.addWidget(saturation_group)

        # 平滑组
        smooth_group = QGroupBox('平滑处理')
        smooth_layout = QHBoxLayout(smooth_group)

        self.smooth_kernel = QSpinBox()
        self.smooth_kernel.setRange(1, 21)
        self.smooth_kernel.setValue(3)
        self.smooth_kernel.setSingleStep(2)
        smooth_layout.addWidget(QLabel('内核大小:'))
        smooth_layout.addWidget(self.smooth_kernel)

        smooth_btn = QPushButton('应用')
        smooth_btn.clicked.connect(self.smooth_image)
        smooth_layout.addWidget(smooth_btn)

        # 图像拼接组
        stitch_group = QGroupBox('图像拼接')
        stitch_layout = QHBoxLayout(stitch_group)

        self.stitch_images_list = QListWidget()
        stitch_layout.addWidget(self.stitch_images_list)

        stitch_controls = QVBoxLayout()
        add_image_btn = QPushButton('添加图像')
        add_image_btn.clicked.connect(self.add_image_for_stitching)
        stitch_controls.addWidget(add_image_btn)

        clear_images_btn = QPushButton('清空')
        clear_images_btn.clicked.connect(self.clear_stitch_images)
        stitch_controls.addWidget(clear_images_btn)

        stitch_btn = QPushButton('开始拼接')
        stitch_btn.clicked.connect(self.stitch_selected_images)
        stitch_controls.addWidget(stitch_btn)

        stitch_layout.addLayout(stitch_controls)

        layout.addWidget(stitch_group)

        # 添加到选项卡
        self.tabs.addTab(tab, '图像处理')

    def create_cv_tab(self):
        """创建计算机视觉选项卡"""
        tab = QWidget()
        layout = QVBoxLayout(tab)

        # 形状识别组
        shapes_group = QGroupBox('形状识别')
        shapes_layout = QHBoxLayout(shapes_group)

        shapes_btn = QPushButton('检测形状')
        shapes_btn.clicked.connect(self.detect_shapes)
        shapes_layout.addWidget(shapes_btn)

        layout.addWidget(shapes_group)

        # 人脸识别组
        faces_group = QGroupBox('人脸识别')
        faces_layout = QVBoxLayout(faces_group)

        # 人脸模板列表
        faces_layout.addWidget(QLabel('人脸模板:'))
        self.face_templates_list = QListWidget()
        self.face_templates_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.face_templates_list.customContextMenuRequested.connect(self.show_face_template_menu)
        faces_layout.addWidget(self.face_templates_list)

        # 添加人脸模板按钮
        add_face_template_btn = QPushButton('添加人脸模板')
        add_face_template_btn.clicked.connect(self.load_face_template)
        faces_layout.addWidget(add_face_template_btn)

        # 从当前图像提取人脸作为模板
        extract_face_btn = QPushButton('从当前图像提取人脸')
        extract_face_btn.clicked.connect(self.extract_face_from_image)
        faces_layout.addWidget(extract_face_btn)

        # 检测人脸按钮
        detect_faces_btn = QPushButton('检测人脸')
        detect_faces_btn.clicked.connect(self.detect_faces)
        faces_layout.addWidget(detect_faces_btn)

        layout.addWidget(faces_group)

        # 添加到选项卡
        self.tabs.addTab(tab, '计算机视觉')

    def create_video_tab(self):
        """创建视频处理选项卡"""
        tab = QWidget()
        layout = QVBoxLayout(tab)

        # 视频控制组
        video_group = QGroupBox('视频控制')
        video_layout = QHBoxLayout(video_group)

        self.play_btn = QPushButton('播放')
        self.play_btn.clicked.connect(self.play_video)
        video_layout.addWidget(self.play_btn)

        self.pause_btn = QPushButton('暂停')
        self.pause_btn.clicked.connect(self.pause_video)
        self.pause_btn.setEnabled(False)
        video_layout.addWidget(self.pause_btn)

        self.stop_btn = QPushButton('停止')
        self.stop_btn.clicked.connect(self.stop_video)
        self.stop_btn.setEnabled(False)
        video_layout.addWidget(self.stop_btn)

        layout.addWidget(video_group)

        # 实时处理组
        realtime_group = QGroupBox('实时处理')
        realtime_layout = QVBoxLayout(realtime_group)

        self.processing_functions = {
            '无处理': None,
            '形状识别': self.image_processor.detect_shapes,
            '人脸识别': self.image_processor.detect_faces
        }

        self.processing_combo = QComboBox()
        self.processing_combo.addItems(list(self.processing_functions.keys()))
        self.processing_combo.currentTextChanged.connect(self.set_processing_function)
        realtime_layout.addWidget(QLabel('处理函数:'))
        realtime_layout.addWidget(self.processing_combo)

        self.enable_processing = QPushButton('启用处理')
        self.enable_processing.clicked.connect(self.toggle_processing)
        self.enable_processing.setEnabled(False)
        realtime_layout.addWidget(self.enable_processing)

        layout.addWidget(realtime_group)

        # 添加到选项卡
        self.tabs.addTab(tab, '视频处理')

    def create_video_progress_bar(self):
        """创建视频进度条"""
        # 创建视频进度条组件
        progress_layout = QHBoxLayout()

        # 时间标签
        self.time_label = QLabel("00:00/00:00")
        progress_layout.addWidget(self.time_label)

        # 百分比标签
        self.percentage_label = QLabel("0%")
        progress_layout.addWidget(self.percentage_label)

        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(0)
        progress_layout.addWidget(self.progress_bar)

        # 将进度条布局添加到视频控制组
        video_group = self.findChild(QGroupBox, "视频控制")  # 找到视频控制组
        if video_group:
            video_layout = video_group.layout()
            video_layout.insertLayout(0, progress_layout)  # 在播放按钮之前插入进度条

    def load_image(self):
        """加载图像"""
        file_path, _ = QFileDialog.getOpenFileName(self, '打开图像', '', '图像文件 (*.png *.jpg *.jpeg *.bmp)')
        if file_path:
            if self.image_processor.load_image(file_path):
                self.display_image(self.image_processor.image)
                self.statusBar().showMessage(f'已加载图像: {os.path.basename(file_path)}')
            else:
                QMessageBox.warning(self, '错误', '无法加载图像')

    def load_images_for_stitching(self):
        """加载多张图像用于拼接"""
        file_paths, _ = QFileDialog.getOpenFileNames(self, '打开图像', '', '图像文件 (*.png *.jpg *.jpeg *.bmp)')
        if file_paths:
            self.stitch_images = []
            self.stitch_images_list.clear()

            for file_path in file_paths:
                img = cv2.imread(file_path)
                if img is not None:
                    self.stitch_images.append(img)
                    self.stitch_images_list.addItem(os.path.basename(file_path))

            if len(self.stitch_images) >= 2:
                self.statusBar().showMessage(f'已加载 {len(self.stitch_images)} 张图像用于拼接')
            else:
                QMessageBox.warning(self, '提示', '至少需要选择两张图像进行拼接')

    def add_image_for_stitching(self):
        """添加单张图像用于拼接"""
        file_path, _ = QFileDialog.getOpenFileName(self, '打开图像', '', '图像文件 (*.png *.jpg *.jpeg *.bmp)')
        if file_path:
            img = cv2.imread(file_path)
            if img is not None:
                self.stitch_images.append(img)
                self.stitch_images_list.addItem(os.path.basename(file_path))
                self.statusBar().showMessage(f'已添加图像: {os.path.basename(file_path)}')

    def clear_stitch_images(self):
        """清空拼接图像列表"""
        self.stitch_images = []
        self.stitch_images_list.clear()
        self.statusBar().showMessage('已清空图像列表')

    def stitch_selected_images(self):
        """拼接选中的图像"""
        if len(self.stitch_images) < 2:
            QMessageBox.warning(self, '提示', '至少需要两张图像进行拼接')
            return

        result = self.image_processor.stitch_images(self.stitch_images)
        if result is not None:
            self.image_processor.image = result
            self.image_processor.processed_image = result
            self.display_image(result)
            self.statusBar().showMessage('图像拼接成功')
        else:
            QMessageBox.warning(self, '错误', '图像拼接失败，请确保图像有足够的重叠区域')

    def load_video(self):
        """加载视频"""
        file_path, _ = QFileDialog.getOpenFileName(self, '打开视频', '', '视频文件 (*.mp4 *.avi *.mov *.mkv)')
        if file_path:
            if self.video_processor.load_video(file_path):
                if self.video_processor.use_opencv:
                    # 使用OpenCV播放
                    self.statusBar().showMessage(f'已加载视频: {os.path.basename(file_path)} (使用OpenCV)')

                    # 停止任何现有的处理线程
                    if self.processing_thread and self.processing_thread.isRunning():
                        self.processing_thread.stop()

                    # 创建新的处理线程
                    self.processing_thread = ProcessingThread(self.video_processor)
                    self.processing_thread.frame_ready.connect(self.display_image)
                    self.processing_thread.progress_updated.connect(self.update_video_progress)
                    self.processing_thread.start()
                else:
                    # 使用QMediaPlayer播放
                    self.video_processor.player.setVideoOutput(self.display_widget)
                    self.statusBar().showMessage(f'已加载视频: {os.path.basename(file_path)}')

                self.play_btn.setEnabled(True)
                self.pause_btn.setEnabled(True)
                self.stop_btn.setEnabled(True)
                self.enable_processing.setEnabled(True)
            else:
                QMessageBox.warning(self, '错误', '无法加载视频')

    def load_face_template(self):
        """加载人脸模板"""
        file_path, _ = QFileDialog.getOpenFileName(self, '打开人脸模板', '', '图像文件 (*.png *.jpg *.jpeg *.bmp)')
        if file_path:
            name, ok = QInputDialog.getText(self, '输入名称', '请输入人脸模板名称:')
            if ok and name:
                if self.image_processor.load_face_template(name, file_path):
                    self.face_templates_list.addItem(name)
                    self.statusBar().showMessage(f'已添加人脸模板: {name}')
                else:
                    QMessageBox.warning(self, '错误', '无法识别模板中的人脸')
            else:
                QMessageBox.information(self, '提示', '模板名称不能为空')

    def show_face_template_menu(self, pos):
        """显示人脸模板右键菜单"""
        if not self.face_templates_list.selectedItems():
            return

        menu = QMenu()
        delete_action = menu.addAction('删除模板')
        delete_action.triggered.connect(self.delete_face_template)

        menu.exec_(self.face_templates_list.mapToGlobal(pos))

    def delete_face_template(self):
        """删除选中的人脸模板"""
        selected_items = self.face_templates_list.selectedItems()
        if not selected_items:
            return

        for item in selected_items:
            name = item.text()
            if name in self.image_processor.face_templates:
                del self.image_processor.face_templates[name]
                self.face_templates_list.takeItem(self.face_templates_list.row(item))

        self.statusBar().showMessage('已删除选中的人脸模板')

    def extract_face_from_image(self):
        """从当前图像提取人脸作为模板"""
        if self.image_processor.image is None:
            QMessageBox.warning(self, '错误', '当前无图像可提取')
            return

        face_features = self.image_processor.extract_face_features()
        if not face_features:
            QMessageBox.warning(self, '提示', '未检测到人脸')
            return

        dialog = FaceTemplateDialog(face_features, self)
        if dialog.exec_():
            selected_index = dialog.selected_index
            if selected_index >= 0 and selected_index < len(face_features):
                name, ok = QInputDialog.getText(self, '输入名称', '请输入人脸模板名称:')
                if ok and name:
                    self.image_processor.face_templates[name] = face_features[selected_index]
                    self.face_templates_list.addItem(name)
                    self.statusBar().showMessage(f'已从图像提取人脸模板: {name}')

    def save_image(self):
        """保存图像"""
        if self.image_processor.processed_image is None:
            QMessageBox.warning(self, '错误', '没有可保存的图像')
            return

        file_path, _ = QFileDialog.getSaveFileName(self, '保存图像', '', '图像文件 (*.png *.jpg *.jpeg *.bmp)')
        if file_path:
            cv2.imwrite(file_path, self.image_processor.processed_image)
            self.statusBar().showMessage(f'已保存图像: {os.path.basename(file_path)}')

    def rotate_image(self, angle=None):
        """旋转图像"""
        if angle is None:
            angle = self.rotate_angle.value()
        self.image_processor.rotate(angle)
        self.display_image(self.image_processor.processed_image)

    def scale_image(self):
        """缩放图像"""
        scale = self.scale_percent.value()
        self.image_processor.scale(scale)
        self.display_image(self.image_processor.processed_image)

    def start_crop(self):
        """开始裁剪"""
        self.is_cropping = True
        self.statusBar().showMessage('请在图像上拖动鼠标选择裁剪区域')

    def confirm_crop(self):
        """确认裁剪"""
        if self.crop_start and self.crop_end and self.is_cropping:
            x = min(self.crop_start.x(), self.crop_end.x())
            y = min(self.crop_start.y(), self.crop_end.y())
            w = abs(self.crop_start.x() - self.crop_end.x())
            h = abs(self.crop_start.y() - self.crop_end.y())

            if w > 0 and h > 0:
                self.image_processor.crop(x, y, w, h)
                self.display_image(self.image_processor.processed_image)
                self.statusBar().showMessage('图像已裁剪')

        self.is_cropping = False
        self.crop_start = None
        self.crop_end = None

    def adjust_brightness_contrast(self):
        """调整亮度和对比度"""
        brightness = self.brightness_slider.value()
        contrast = self.contrast_slider.value()
        self.brightness_value.setText(str(brightness))
        self.contrast_value.setText(str(contrast))
        self.image_processor.adjust_brightness_contrast(brightness, contrast)
        self.display_image(self.image_processor.processed_image)

    def adjust_saturation(self):
        """调整饱和度"""
        value = self.saturation_slider.value()
        self.saturation_value.setText(f'{value}%')
        self.image_processor.adjust_saturation(value)
        self.display_image(self.image_processor.processed_image)

    def smooth_image(self):
        """平滑图像"""
        kernel_size = self.smooth_kernel.value()
        if kernel_size % 2 == 0:  # 确保是奇数
            kernel_size += 1
            self.smooth_kernel.setValue(kernel_size)
        self.image_processor.smooth(kernel_size)
        self.display_image(self.image_processor.processed_image)

    def detect_shapes(self):
        """检测形状"""
        self.image_processor.detect_shapes()
        self.display_image(self.image_processor.processed_image)
        self.statusBar().showMessage(f'已检测到 {len(self.image_processor.detected_shapes)} 个形状')

    def detect_faces(self):
        """检测人脸"""
        self.image_processor.detect_faces()
        self.display_image(self.image_processor.processed_image)
        self.statusBar().showMessage(f'已检测到 {len(self.image_processor.faces)} 个人脸')

    def play_video(self):
        """播放视频"""
        if self.video_processor.play():
            self.play_btn.setEnabled(False)
            self.pause_btn.setEnabled(True)
            self.statusBar().showMessage('视频播放中')

    def pause_video(self):
        """暂停视频"""
        self.video_processor.pause()
        self.play_btn.setEnabled(True)
        self.pause_btn.setEnabled(False)
        self.statusBar().showMessage('视频已暂停')

    def stop_video(self):
        """停止视频"""
        self.video_processor.stop()
        self.play_btn.setEnabled(True)
        self.pause_btn.setEnabled(False)
        self.stop_btn.setEnabled(False)
        self.statusBar().showMessage('视频已停止')

    def set_processing_function(self, func_name):
        """设置视频处理函数"""
        func = self.processing_functions.get(func_name, None)
        self.video_processor.set_processing_function(func)
        self.statusBar().showMessage(f'已设置处理函数: {func_name}')

    def toggle_processing(self):
        """切换视频处理状态"""
        self.video_processor.toggle_processing(not self.video_processor.processing_enabled)
        if self.video_processor.processing_enabled:
            self.enable_processing.setText('禁用处理')
            self.statusBar().showMessage('已启用实时处理')
        else:
            self.enable_processing.setText('启用处理')
            self.statusBar().showMessage('已禁用实时处理')

    def toggle_camera(self):
        """切换摄像头状态"""
        if not self.video_capture:
            self.video_capture = cv2.VideoCapture(0)
            if not self.video_capture.isOpened():
                QMessageBox.warning(self, '错误', '无法打开摄像头')
                self.video_capture = None
                return

            self.video_processor.use_opencv = True
            self.processing_thread = ProcessingThread(self.video_processor)
            self.processing_thread.frame_ready.connect(self.display_image)
            self.processing_thread.start()
            self.play_btn.setEnabled(False)
            self.pause_btn.setEnabled(False)
            self.stop_btn.setEnabled(True)
            self.enable_processing.setEnabled(True)
            self.statusBar().showMessage('摄像头已打开')
        else:
            self.video_capture.release()
            self.video_capture = None
            if self.processing_thread and self.processing_thread.isRunning():
                self.processing_thread.stop()
            self.play_btn.setEnabled(False)
            self.pause_btn.setEnabled(False)
            self.stop_btn.setEnabled(False)
            self.enable_processing.setEnabled(False)
            self.statusBar().showMessage('摄像头已关闭')

    def toggle_fullscreen(self):
        """切换全屏状态"""
        if self.isFullScreen():
            self.showNormal()
        else:
            self.showFullScreen()

    def undo(self):
        """撤销上一步操作"""
        if self.image_processor.undo():
            self.display_image(self.image_processor.processed_image)
            self.statusBar().showMessage('已撤销上一步操作')
        else:
            self.statusBar().showMessage('没有可撤销的操作')

    def video_state_changed(self, state):
        """视频状态变化处理"""
        if state == QMediaPlayer.PlayingState:
            self.play_btn.setEnabled(False)
            self.pause_btn.setEnabled(True)
            self.statusBar().showMessage('视频播放中')
        elif state == QMediaPlayer.PausedState:
            self.play_btn.setEnabled(True)
            self.pause_btn.setEnabled(False)
            self.statusBar().showMessage('视频已暂停')
        elif state == QMediaPlayer.StoppedState:
            self.play_btn.setEnabled(True)
            self.pause_btn.setEnabled(False)
            self.stop_btn.setEnabled(False)
            self.statusBar().showMessage('视频已停止')

    def video_error(self, error):
        """视频错误处理"""
        self.statusBar().showMessage(f'视频播放错误: {self.video_processor.player.errorString()}')
        QMessageBox.warning(self, '视频错误', f'播放视频时发生错误: {self.video_processor.player.errorString()}')

    def update_video_progress(self, current_frame, total_frames):
        """更新视频进度"""
        if total_frames > 0:
            percentage = int(current_frame * 100 / total_frames)
            self.progress_bar.setValue(percentage)
            self.percentage_label.setText(f"{percentage}%")

            # 计算时间
            current_seconds = int(current_frame / self.video_processor.fps)
            total_seconds = int(total_frames / self.video_processor.fps)

            current_time = f"{current_seconds // 60:02d}:{current_seconds % 60:02d}"
            total_time = f"{total_seconds // 60:02d}:{total_seconds % 60:02d}"

            self.time_label.setText(f"{current_time}/{total_time}")

    def display_image(self, image):
        """显示图像"""
        if image is None:
            return

        # 将OpenCV图像转换为Qt图像
        height, width, channel = image.shape
        bytes_per_line = 3 * width
        q_image = QImage(image.data, width, height, bytes_per_line, QImage.Format_BGR888)

        # 显示图像
        self.display_widget.setImage(q_image)

    def mousePressEvent(self, event):
        """鼠标按下事件"""
        if self.is_cropping and event.button() == Qt.LeftButton:
            self.crop_start = event.pos()
            self.crop_end = None

    def mouseMoveEvent(self, event):
        """鼠标移动事件"""
        if self.is_cropping and self.crop_start and event.buttons() & Qt.LeftButton:
            self.crop_end = event.pos()
            # 这里可以实现实时预览裁剪区域

    def mouseReleaseEvent(self, event):
        """鼠标释放事件"""
        if self.is_cropping and self.crop_start and event.button() == Qt.LeftButton:
            self.crop_end = event.pos()
            self.statusBar().showMessage('请点击"确认裁剪"按钮完成裁剪')


if __name__ == '__main__':
    # 确保中文显示正常
    os.environ['QT_FONT_DPI'] = '96'

    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())